<template>
    <div class="f_col" style="width: 100%; height: 100%; align-items: flex-start">
        <div
            style="
                padding: 0;
                gap: 12px;
                width: 100%;
                height: 26px;
                background-color: var(--page-bg-color);
                justify-content: flex-start;
                padding-left: 6px;
            "
            class="f_row"
        >
            <span style="font-size: 12px; width: 32px; color: #817d7d">序号 </span>
            <a-tooltip v-if="props.func.copy">
                <template #title>复制</template>
                <copy-one @click="copy" theme="outline" size="14" fill="#1890ff" strokeLinecap="square" />
            </a-tooltip>
            <a-tooltip v-if="props.func.clear">
                <template #title>清空</template>
                <delete-four @click="clear" theme="outline" size="14" fill="#1890ff" strokeLinecap="square" />
            </a-tooltip>
            <a-tooltip v-if="props.func.sortTxt">
                <template #title>字符串排序</template>
                <sort-one @click="sortTxt" theme="outline" size="14" fill="#1890ff" strokeLinecap="square" />
            </a-tooltip>
            <a-tooltip v-if="props.func.clearline">
                <template #title>清除空行</template>
                <clear-format @click="clearline" theme="outline" size="14" fill="#1890ff" strokeLinecap="square" />
            </a-tooltip>
            <a-tooltip v-if="props.func.clearlineBlank">
                <template #title>清除前后空格</template>
                <erase theme="outline" @click="clearlineBlank" size="14" fill="#1890ff" strokeLinecap="square" />
            </a-tooltip>
            <a-tooltip v-if="props.func.expandJson">
                <template #title>格式化-展开</template>
                <expand-text-input
                    @click="expandJson"
                    theme="outline"
                    size="14"
                    fill="#1890ff"
                    strokeLinecap="square"
                />
            </a-tooltip>
            <a-tooltip v-if="props.func.collapseJson">
                <template #title>格式化-压缩</template>
                <collapse-text-input
                    @click="collapseJson"
                    theme="outline"
                    size="14"
                    fill="#1890ff"
                    strokeLinecap="square"
                />
            </a-tooltip>
            <a-checkbox-group
                v-if="props.func.tags"
                v-model:value="state.tags"
                name=""
                class="f_row"
                @change="filterLineNumbers"
                :options="[
                    { label: '红', value: 'color_red' },
                    { label: '绿', value: 'color_limegreen' },
                    { label: '黄', value: 'color_yellow' },
                    { label: '空白', value: 'white' },
                ]"
            >
                <template #label="{ label }">
                    <template v-if="label === '红'">
                        <span style="color: red; font-size: 13px" class="unselectable">{{ label }}</span>
                    </template>
                    <template v-else-if="label === '绿'">
                        <span style="color: limegreen; font-size: 13px" class="unselectable">{{ label }}</span>
                    </template>
                    <template v-else-if="label === '黄'">
                        <span style="color: black; font-size: 13px; background-color: yellow" class="unselectable">{{
                            label
                        }}</span>
                    </template>
                    <template v-else>
                        <span class="unselectable" style="font-size: 13px; color: var(--character-60)">{{
                            label
                        }}</span>
                    </template>
                </template>
            </a-checkbox-group>
            <a-input
                v-if="props.func.remark"
                v-model:value="state.remark"
                style="min-width: 60px; flex: 1"
                placeholder="备注..."
                size="small"
            />
            <div style="width: 20px"></div>
        </div>
        <div class="editor-container" ref="con">
            <div class="line-numbers" ref="lineNumbers">
                <div
                    v-for="line in state.content_list"
                    :key="line.value"
                    :class="line.color"
                    :style="{ height: line.height + 'px' }"
                >
                    {{ line.value }}
                </div>
            </div>
            <textarea
                v-model="state.content"
                ref="editor"
                class="editor"
                @scroll="handleScroll"
                @input="updateLineNumbers_lock"
                @change="changeTxt"
                :disabled="props.disabled"
            ></textarea>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, watchEffect, reactive, nextTick, watch } from "vue"
import {
    CopyOne,
    DeleteFour,
    SortOne,
    ClearFormat,
    Erase,
    Undo,
    ExpandTextInput,
    CollapseTextInput,
} from "@icon-park/vue-next"
import useClipboard from "vue-clipboard3"
import { message } from "ant-design-vue"
import { debounceRef } from "@/utils/common.js"

const { toClipboard } = useClipboard()
const editor = ref(null)
const lineNumbers = ref(null)
const con = ref(null)
const props = defineProps({
    func: {
        type: Object,
        default: {}, // { copy: true, clear: true, sortTxt: true, clearline: true, clearlineBlank: true, expandJson: true, collapseJson: true, tags: true, remark: true }
    },
    disabled: {
        type: Boolean,
        default: false,
    },
})

const copy = async () => {
    try {
        // 复制
        await toClipboard(state.content)
        message.success("复制成功")
        // 复制成功
    } catch (e) {
        // 复制失败
    }
}

function sortTxt() {
    state.content = state.content.split("\n").sort().join("\n")
}

function clear() {
    // 清空
    state.content = ""
    state.content_list = [{ value: 1, text: "", height: 24, color: "" }]
    state.content_map = {}
}

function clearline() {
    // 清除空行
    state.content = state.content.replace(/^\s*[\r\n]/gm, "")
}

function clearlineBlank() {
    // 清除每一行的前后空格
    state.content = state.content.replace(/^\s*|\s*$/g, "")
}
function sortObjectKeys(obj) {
    if (Array.isArray(obj)) {
        return obj.map(sortObjectKeys)
    } else if (typeof obj === "object" && obj !== null) {
        return Object.keys(obj)
            .sort()
            .reduce((sortedObj, key) => {
                sortedObj[key] = sortObjectKeys(obj[key])
                return sortedObj
            }, {})
    } else {
        return obj
    }
}
function expandJson() {
    // 格式化-展开
    try {
        state.data = JSON.parse(state.content)
        let sortedObj = sortObjectKeys(state.data)

        state.content = JSON.stringify(sortedObj, null, 4)
    } catch (e) {
        message.error("JSON格式错误:" + e)
        state.data = {}
    }
}
function collapseJson() {
    // 格式化-压缩
    try {
        state.data = JSON.parse(state.content)
        state.content = JSON.stringify(state.data)
    } catch (e) {
        message.error("JSON格式错误:" + e)
        state.data = {}
    }
}

function filterLineNumbers() {
    editor.value.scrollTop = 0
    lineNumbers.value.scrollTop = 0

    if (state.tags.length == 0 || state.tags.length == 4) {
        state.content = state.source_content
        editor.value.disabled = false || props.disabled
        state.content_list = state.source_list
    } else {
        const list = []
        let keys = Object.keys(state.content_map)
        let txt = ""
        for (let i = 0; i < keys.length; i++) {
            if (state.content_map[keys[i]]) {
                if (state.tags.includes(state.content_map[keys[i]].color)) {
                    list.push(state.content_map[keys[i]])
                    txt += state.content_map[keys[i]].text + "\n"
                }
            }
        }
        state.content_list = list
        state.content = txt
        editor.value.disabled = true
    }
}

const handleScroll = () => {
    lineNumbers.value.scrollTop = editor.value.scrollTop
}
const state = reactive({
    height: 0,
    remark: "",
    content: "",
    source_content: "",
    source_list: [],
    color_red_index: [],
    color_limegreen_index: [],
    color_yellow_index: [],
    content_map: {},
    content_list: [{ value: 1, text: "", height: 24, color: "" }],
    tags: [],
})
const content = ref("")
onMounted(() => {
    nextTick(() => {
        state.height = con.value.clientHeight || 0
    })
    window.addEventListener("resize", () => {
        nextTick(() => {
            state.height = con.value.clientHeight
        })
    })
})
function setContent(data) {
    if (state.content != data) {
        state.content = data
        updateLineNumbers_lock()
    }
}
function getContent() {
    return state.content
}
function setColor(data) {
    if (data.red) {
        state.color_red_index = data.red || []
    }
    if (data.green) {
        state.color_limegreen_index = data.green || []
    }
    if (data.yellow) {
        state.color_yellow_index = data.yellow || []
    }
    updateLineNumbers_lock()
}
defineExpose({
    setContent,
    getContent,
    setColor,
})

function getTextHeight(text) {
    if (!text || text.length < 20) {
        return 24
    }
    let editor_width = editor.value.offsetWidth
    if (editor_width / 20 > text.length) {
        return 24
    }
    const textarea = document.createElement("textarea")
    textarea.style.width = editor_width + "px"
    textarea.style.fontFamily = "monospace"
    textarea.style.fontSize = "14px"
    textarea.style.lineHeight = "24px"
    textarea.style.padding = "0 12px"
    textarea.style.position = "absolute"
    textarea.style.visibility = "hidden"
    textarea.style.left = "-9999px"
    textarea.style.top = "-9999px"
    textarea.style.height = "24px"
    textarea.style.resize = "none"
    textarea.style.wordBreak = "break-all"
    textarea.innerHTML = text
    document.body.appendChild(textarea)
    const height = textarea.scrollHeight

    document.body.removeChild(textarea)
    return height
}

const lock = debounceRef(0)

function updateLineNumbers_lock() {
    if (state.content?.split("\n").length > 500) {
        updateLineNumbers()
    } else {
        lock.value += 1
    }
}
watch(
    () => lock.value,
    (val) => {
        if (val > 0) {
            updateLineNumbers()
            lock.value = 0
        }
    },
)

const updateLineNumbers = () => {
    if (con.value && con.value.nodeType === Node.ELEMENT_NODE && state.tags.length == 0) {
        // const lineHeight = 24
        // let numberOfLines = Math.floor(state.height / lineHeight)
        const contentList = state.content.split("\n")
        const con_len = contentList.length
        const tempData = {}
        const tempList = []
        state.source_content = state.content
        for (var i = 0; i < con_len; i++) {
            let color = "white"
            if (state.color_red_index.includes(i + 1)) {
                color = "color_red"
            } else if (state.color_limegreen_index.includes(i + 1)) {
                color = "color_limegreen"
            } else if (state.color_yellow_index.includes(i + 1)) {
                color = "color_yellow"
            }
            if (state.content_map[i]) {
                if (state.content_map[i].text !== contentList[i]) {
                    const da = {
                        height: getTextHeight(contentList[i]),
                        text: contentList[i],
                        value: i + 1,
                        color: color,
                    }
                    tempData[i] = da
                    tempList.push(da)
                } else {
                    tempData[i] = state.content_map[i]
                    tempData[i].color = color
                    tempList.push(tempData[i])
                }
            } else {
                const da = { height: getTextHeight(contentList[i]), text: contentList[i], value: i + 1, color: color }
                tempData[i] = da
                tempList.push(da)
            }
        }

        state.content_map = tempData
        state.content_list = tempList
        state.source_list = [...tempList]
    }
}
onMounted(() => {
    updateLineNumbers()
})
const emit = defineEmits(["change"])
function changeTxt() {
    emit("change", state.content)
}
// watchEffect(() => {
//     updateLineNumbers()
// })
</script>

<style scoped>
.editor-container {
    height: 100%;
    overflow: hidden;
    width: 100%;
    position: relative;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    background-color: var(--element-bg-color);
    border-radius: 8px;
    border: 1px solid var(--Neutral-10);
}

.line-numbers {
    flex: 1;
    background-color: var(--Neutral-11);
    padding-right: 4px;
    box-sizing: border-box;
    user-select: none;
    height: 100%;
    overflow-y: hidden;
    padding-top: 1px;
    padding-left: 6px;
    line-height: 24px;
    text-align: center;
    position: relative;
    font-size: 12px;
    font-family: monospace;
    color: var(--character-45);
    border-right: 1px solid var(--Neutral-10);
}

.editor {
    width: calc(100% - 50px);
    border: none;
    resize: none;
    padding: 0 16px;
    height: 100%;
    word-wrap: break-word;
    word-break: break-all;
    white-space: pre-wrap;
    background-color: var(--element-bg-color);
    color: var(--character-85);
    font-family: monospace;
    line-height: 24px;
}

.editor:focus {
    outline: none;
}

:deep(.ant-checkbox-wrapper) {
    color: var(--character-75);
}

:deep(.ant-checkbox-checked .ant-checkbox-inner) {
    background-color: var(--Geek-Blue-5);
    border-color: var(--Geek-Blue-5);
}

:deep(.ant-input) {
    background-color: var(--Neutral-11);
    border: 1px solid var(--Neutral-10);
    color: var(--character-85);
}

:deep(.ant-input:hover) {
    border-color: var(--Neutral-9);
}

:deep(.ant-input:focus) {
    border-color: var(--Geek-Blue-5);
    box-shadow: 0 0 0 2px rgba(89, 126, 247, 0.1);
}
textarea {
    line-height: 24px !important;
}
</style>
<style>
.line-numbers .color_red {
    background-color: red;
    color: white;
}
.line-numbers .color_limegreen {
    background-color: limegreen;
    color: white;
}
.line-numbers .color_yellow {
    background-color: yellow;
    color: white;
}
</style>
